package com.iwdnb.gkgz.application.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.iwdnb.bmnf.common.eventbus.EventBus;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.bmnf.common.utils.BooleanUtils;
import com.iwdnb.bmnf.common.utils.ReflectUtils;
import com.iwdnb.bmnf.common.utils.UUID;
import com.iwdnb.gkgz.application.service.StockPoolService;
import com.iwdnb.gkgz.application.service.StockPredictionService;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockPredictionData;
import com.iwdnb.gkgz.common.model.dto.prediction.StockIncomePrediction;
import com.iwdnb.gkgz.common.model.dto.prediction.StockPredictionParam;
import com.iwdnb.gkgz.common.model.dto.prediction.StockPredictionResult;
import com.iwdnb.gkgz.common.model.dto.prediction.StockPredictionStatistics;
import com.iwdnb.gkgz.common.model.dto.prediction.StockPredictionYearIncome;
import com.iwdnb.gkgz.common.utils.StockIncomeUtils;
import com.iwdnb.gkgz.common.utils.StockPredictionUtils;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockDayDataRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockPredictionParamRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockPredictionResultRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import static com.iwdnb.gkgz.application.event.DynamicPredictionEventHandler.TOPIC_DYNAMIC_PREDICTION;

@Component
@Slf4j
public class StockPredictionServiceImpl implements StockPredictionService {
    @Autowired
    private StockPoolService stockPoolService;
    @Autowired
    private StockDayDataRepository stockDayDataRepository;
    @Autowired
    private StockPredictionResultRepository stockPredictionResultRepository;
    @Autowired
    private StockPredictionParamRepository stockPredictionParamRepository;
    @Autowired
    private Executor executor;

    @Override
    public List<StockPredictionData> guessTomorrow(String year) {
        List<Stock> stockList = stockPoolService.queryStockList();
        List<String> codes = getCodes(stockList);
        List<StockPredictionData> tomorrowPredictions = new ArrayList<>();
        for (String code : codes) {
            List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByYear(code, year);
            stockDayDataList = StockPredictionUtils.predictionStock(stockDayDataList);
            tomorrowPredictions.add(stockDayDataList.get(stockDayDataList.size() - 1).getTomorrowPrediction());

        }
        System.out.println("#################以下为预测结果,工作日下午2.50分以后有效##########################");
        log.info(JSON.toJSONString(tomorrowPredictions));
        return tomorrowPredictions;
    }

    @Override
    public List<StockPredictionData> guessAfterTomorrow(String year) {
        List<StockPredictionData> afterTomorrowPredictions = new ArrayList<>();
        List<Stock> stockList = stockPoolService.queryStockList();
        List<String> codes = getCodes(stockList);
        for (String code : codes) {
            List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByYear(code, year);
            StockDayData todayData = stockDayDataList.get(stockDayDataList.size() - 1);
            stockDayDataList = StockPredictionUtils.predictionStock(stockDayDataList);
            //假设明天精准匹配，预测后天数据
            StockDayData tomorrowData = BeanConvertUtils.convert(todayData.getTomorrowPrediction(), StockDayData.class);
            tomorrowData.setOpenPrice(tomorrowData.getMaxPrice());
            tomorrowData.setClosePrice(tomorrowData.getMinPrice());
            stockDayDataList = Lists.newArrayList(todayData, tomorrowData);
            stockDayDataList = StockPredictionUtils.predictionStock(stockDayDataList);
            afterTomorrowPredictions.add(tomorrowData.getTomorrowPrediction());
        }
        System.out.println("#################以下为后日预测结果,工作日下午2.50分以后有效##########################");
        log.info(JSON.toJSONString(afterTomorrowPredictions));
        return afterTomorrowPredictions;
    }

    @Override
    public void generatePredictionParam() {
        StockPredictionParam defaultParam = getDefaultPredictionParam();
        BigDecimal onePer = new BigDecimal("0.01");
        BigDecimal threePer = new BigDecimal("0.03");

        BigDecimal tenPer = new BigDecimal("0.1");
        BigDecimal twentyPer = new BigDecimal("0.2");
        BigDecimal thirtyPer = new BigDecimal("0.3");

        List<StockPredictionParam> params = new ArrayList<>();
        StockPredictionParam param = BeanConvertUtils.convert(defaultParam, StockPredictionParam.class);
        for (int a = 1; a <= 5; a++) {
            param.setMaxTotalHighDivideNum(
                defaultParam.getMaxTotalHighDivideNum().add(onePer.multiply(new BigDecimal(a))));
            for (int c = 1; c <= 3; c++) {
                param.setMaxRangeRate(defaultParam.getMaxRangeRate().add(thirtyPer.multiply(new BigDecimal(c))));
                for (int d = 1; d <= 4; d++) {
                    param.setMaxRangeFirstRate(
                        defaultParam.getMaxRangeFirstRate().add(thirtyPer.multiply(new BigDecimal(d))));
                    for (int e = 1; e <= 4; e++) {
                        param.setMaxRangeSecondRate(
                            defaultParam.getMaxRangeSecondRate().add(thirtyPer.multiply(new BigDecimal(e))));
                        for (int ma = 1; ma <= 5; ma++) {
                            param.setMinTotalHighDivideNum(
                                defaultParam.getMinTotalHighDivideNum().add(threePer.multiply(new BigDecimal(ma))));
                            for (int mc = 1; mc <= 3; mc++) {
                                param.setMinRangeRate(
                                    defaultParam.getMinRangeRate().add(thirtyPer.multiply(new BigDecimal(mc))));
                                for (int md = 1; md <= 4; md++) {
                                    param.setMinRangeFirstRate(
                                        defaultParam.getMinRangeFirstRate()
                                            .add(thirtyPer.multiply(new BigDecimal(md))));
                                    for (int me = 1; me <= 4; me++) {
                                        param.setMinRangeSecondRate(
                                            defaultParam.getMinRangeSecondRate()
                                                .add(thirtyPer.multiply(new BigDecimal(me))));
                                        param.setUuid(UUID.fastUUID().toString());
                                        param.setPredictionFlag(BooleanUtils.FALSE);
                                        param.setTag();
                                        params.add(param);
                                        param = BeanConvertUtils.convert(param,
                                            StockPredictionParam.class);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        stockPredictionParamRepository.insert(params);
    }

    @Override
    public void dynamicPrediction(int begin, String sort, Table<String, String, List<StockDayData>> table) {
        List<StockPredictionParam> stockPredictionParams = stockPredictionParamRepository.queryNotPredictionParams(
            begin,
            sort);
        if (CollectionUtils.isEmpty(stockPredictionParams)) {
            return;
        }
        if (Objects.isNull(table)) {
            table = getCodeYearData();
        }
        for (StockPredictionParam param : stockPredictionParams) {
            param.setPredictionFlag("t");
        }
        stockPredictionParamRepository.update(stockPredictionParams);
        for (StockPredictionParam param : stockPredictionParams) {
            StockPredictionResult result = caculateIncome(param, table);
            result.setStatistics(null);
            result.setYearIncomes(null);
            stockPredictionResultRepository.insert(result);
            param.setPredictionFlag(BooleanUtils.TRUE);
            stockPredictionParamRepository.update(param);
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("sort", sort);
        jsonObject.put("table", table);
        jsonObject.put("begin", begin);
        EventBus.sendEvent(TOPIC_DYNAMIC_PREDICTION, jsonObject);
    }

    @Override
    public StockPredictionResult caculateIncome(String uuid) {
        StockPredictionParam param = stockPredictionParamRepository.getParamByUUID(uuid);
        return caculateIncome(param, null);
    }

    @Override
    public StockPredictionResult caculateIncome(StockPredictionParam param,
        Table<String, String, List<StockDayData>> table) {
        if (Objects.isNull(table)) {
            table = getCodeYearData();
        }
        List<String> codes = Lists.newArrayList(table.rowKeySet());
        List<String> years = Lists.newArrayList(table.columnKeySet());
        StockPredictionResult predictionResult = new StockPredictionResult();
        predictionResult.setUuid(param.getUuid());
        predictionResult.setParam(param);
        predictionResult.setStrategeName(param.getStrategyName());
        predictionResult.setStockCodes(codes);
        List<Triple<String, String, BigDecimal>> result = new ArrayList<>();
        List<StockPredictionStatistics> statistics = new ArrayList<>();
        predictionResult.setStatistics(statistics);
        for (String code : codes) {
            for (String year : years) {
                List<StockDayData> stockDayDataList = BeanConvertUtils.convertList(table.get(code, year),
                    StockDayData.class);
                //stockDayDataList.add(StockUtils.getRealtimeStockDataList(code));
                stockDayDataList = StockPredictionUtils.predictionStock(stockDayDataList, param);
                //log.info("#################以下计算收益##########################");
                List<StockIncomePrediction> stockIncomePredictions = StockIncomeUtils.caculatePredictionIncome(
                    stockDayDataList);
                //String fileName = SystemUtil.get(SystemUtil.USER_DIR) + "/" + code + "_" + year
                //    + ".xlsx";
                //excelService.exportExcel(stockIncomePredictions, new FileOutputStream(fileName));
                BigDecimal total = StockIncomeUtils.caculatePredictionTotalIncome(stockIncomePredictions);
                Triple<String, String, BigDecimal> pair = new ImmutableTriple<>(code, year, total);
                result.add(pair);
                StockPredictionStatistics statistic = StockPredictionUtils.doPredictionStatistics(stockDayDataList);
                statistics.add(statistic);
                predictionResult.setMatchSellCount(
                    predictionResult.getMatchSellCount() + statistic.getMatchSellCount());
                predictionResult.setMatchBuyCount(predictionResult.getMatchBuyCount() + statistic.getMatchBuyCount());
                predictionResult.setMatchAllCount(predictionResult.getMatchAllCount() + statistic.getMatchAllCount());
                predictionResult.setEqSellCount(predictionResult.getEqSellCount() + statistic.getEqSellCount());
                predictionResult.setEqBuyCount(predictionResult.getEqBuyCount() + statistic.getEqBuyCount());
                predictionResult.setEqAllCount(predictionResult.getEqAllCount() + statistic.getEqAllCount());

                //System.out.println("==================================================");
            }
        }
        //log.info("总收益计算,result:{}", JSON.toJSONString(result));
        String code = null;
        List<StockPredictionYearIncome> yearIncomes = new ArrayList<>();
        StockPredictionYearIncome stockYearIncome = null;
        BigDecimal total = new BigDecimal(0);
        for (Triple<String, String, BigDecimal> triple : result) {
            if (!StringUtils.equals(code, triple.getLeft())) {
                if (StringUtils.isBlank(code)) {
                    code = triple.getLeft();
                }
                stockYearIncome = new StockPredictionYearIncome();
                stockYearIncome.setCode(triple.getLeft());
                yearIncomes.add(stockYearIncome);
            }
            code = triple.getLeft();
            ReflectUtils.setProperty(stockYearIncome, "year" + triple.getMiddle(), triple.getRight());
            total = total.add(triple.getRight());
        }
        predictionResult.setYearIncomes(yearIncomes);
        predictionResult.setTotalIncome(total);
        //String fileName = SystemUtil.get(SystemUtil.USER_DIR) + "/" + System.currentTimeMillis() + ".xlsx";
        //excelService.exportExcel(list, new FileOutputStream(fileName));
        //log.info(total + "");
        return predictionResult;
    }

    private Table<String, String, List<StockDayData>> getCodeYearData() {
        Table<String, String, List<StockDayData>> table = HashBasedTable.create();
        List<Stock> stockList = stockPoolService.queryStockList();
        List<String> codes = getCodes(stockList);
        List<String> years = Lists.newArrayList("2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023");
        for (String code : codes) {
            for (String year : years) {
                List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByYear(code, year);
                table.put(code, year, stockDayDataList);
            }
        }
        return table;
    }

    public static StockPredictionParam getDefaultPredictionParam() {
        StockPredictionParam param = new StockPredictionParam();
        param.setStrategyName("defaultPredictionStrategy");
        param.setMaxTotalHighDivideNum(new BigDecimal("7.90"));
        param.setMaxTotalLowDivideNum(new BigDecimal("8"));
        param.setMaxRangeFirstThreshold(new BigDecimal("0.03"));
        param.setMaxRangeSecondThreshold(new BigDecimal("0.05"));
        param.setMaxRangeRate(new BigDecimal("0.9"));
        param.setMaxRangeFirstRate(new BigDecimal("1.1"));
        param.setMaxRangeSecondRate(new BigDecimal("1.3"));
        param.setMinTotalHighDivideNum(new BigDecimal("7.90"));
        param.setMinTotalLowDivideNum(new BigDecimal("8"));
        param.setMinRangeFirstThreshold(new BigDecimal("0.03"));
        param.setMinRangeSecondThreshold(new BigDecimal("0.05"));
        param.setMinRangeRate(new BigDecimal("0.6"));
        param.setMinRangeFirstRate(new BigDecimal("1"));
        param.setMinRangeSecondRate(new BigDecimal("1.2"));
        return param;
    }

    private List<String> getCodes(List<Stock> stockList) {
        //return Lists.newArrayList("sh600527");
        return stockList.stream().map(stock -> stock.getCode()).collect(
            Collectors.toList());
    }
}
